package fr.inserm.umr915.vcfserver.bio.ucsc.knownGene;

import java.sql.ResultSet;
import java.sql.SQLException;



import org.w3c.dom.Document;
import org.w3c.dom.Element;


import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.bind.tuple.TupleInput;
import com.sleepycat.bind.tuple.TupleOutput;


import fr.inserm.umr915.vcfserver.xml.DomSerializer;
import fr.inserm.umr915.vcfserver.xml.XmlStreamSerializer;


public class KnownGene
	{
	private String name;
	private String chrom;
	private char strand;
	private int txStart;
	private int txEnd;
	private int cdsStart;
	private int cdsEnd;
	private int exonStarts[];
	private int exonEnds[];

	public String getName()
		{
		return name;
		}

	public String getChrom()
		{
		return chrom;
		}
	public char getStrand()
		{
		return strand;
		}

	public boolean isForward()
		{
		return getStrand()=='+';
		}

	public int getTxStart() {
	return txStart;
	}
	public int getTxEnd() {
	return txEnd;
	}
	public int getCdsStart() {
	return cdsStart;
	}
	public int getCdsEnd() {
	return cdsEnd;
	}
	public int[] getExonStarts() {
	return exonStarts;
	}
	public int[] getExonEnds() {
	return exonEnds;
	}

	public int getExonStart(int index)
		{
		return this.exonStarts[index];
		}
	

	public int getExonEnd(int index)
		{
		return this.exonEnds[index];
		}

	public Exon getExon(int index)
		{
		return new Exon(index);
		}
	public Intron getIntron(int i)
		{
		return new Intron(i);
		}
	
	
	abstract class Segment
		{
		private int index;
		protected Segment(int index)
			{
			this.index=index;
			}
	
		public int getIndex()
			{
			return index;
			}
	
		public KnownGene getGene()
			{
			return KnownGene.this;
			}
	
		public boolean contains(int position)
			{
			return getStart()<=position && position< getEnd();
			}
		public abstract boolean isSplicingAcceptor(int position);
		public abstract boolean isSplicingDonor(int position);
		public boolean isSplicing(int position)
			{
			return isSplicingAcceptor(position) || isSplicingDonor(position);
			}
	
		public abstract String getName();
		public abstract int getStart();
		public abstract int getEnd();
		}

	public class Exon extends Segment
		{
		private Exon(int index)
			{
			super(index);
			}
	
		@Override
		public String getName()
			{
			if(getGene().getStrand()=='+')
				{
			return "Exon "+(getIndex()+1);
				}
			else
				{
				return "Exon "+(getGene().getExonCount()-getIndex());
				}
			}
	
		@Override
		public int getStart()
			{
			return getGene().getExonStart(getIndex());
			}
	
		@Override
		public int getEnd()
			{
			return getGene().getExonEnd(getIndex());
			}
	
		@Override
		public String toString()
			{
			return getName();
			}
	
	
		public Intron getNextIntron()
			{
			if(getIndex()+1>=getGene().getExonCount()) return null;
			return getGene().getIntron(getIndex());
			}
		public Intron getPrevIntron()
			{
			if(getIndex()<=0) return null;
			return getGene().getIntron(getIndex()-1);
			}
	
		
		
		@Override
		public boolean isSplicingAcceptor(int position)
			{
			if(!contains(position)) return false;
			if(isForward())
				{
				if(getIndex()== 0) return false;
				return position==getStart();
				}
			else
				{
				if(getIndex()+1== getGene().getExonCount()) return false;
				return position==getEnd()-1;
				}
			}
	
		@Override
		public boolean isSplicingDonor(int position)
			{
			if(!contains(position)) return false;
			if(isForward())
				{
				if(getIndex()+1== getGene().getExonCount()) return false;
				return  (position==getEnd()-1) ||
				(position==getEnd()-2) ||
				(position==getEnd()-3) ;
				}
			else
				{
				if(getIndex()== 0) return false;
				return  (position==getStart()+0) ||
				(position==getStart()+1) ||
				(position==getStart()+2) ;
				}
			}
	
		}

	public class Intron extends Segment
		{
		Intron(int index)
			{
			super(index);
			}
	
		@Override
		public int getStart()
			{
			return getGene().getExonEnd(getIndex());
			}
	
		@Override
		public int getEnd()
			{
			return getGene().getExonStart(getIndex()+1);
			}
	
		@Override
		public String getName()
			{
			if(getGene().isForward())
				{
				return "Intron "+(getIndex()+1);
				}
			else
				{
				return "Intron "+(getGene().getExonCount()-getIndex());
				}
			}
		
		@Override
		public boolean isSplicingAcceptor(int position)
			{
			if(!contains(position)) return false;
			if(isForward())
				{
				return  (position==getEnd()-1) ||
				(position==getEnd()-2);
				}
			else
				{
				return	position==getStart() ||
				position==getStart()+1;
				}
			}
	
		@Override
		public boolean isSplicingDonor(int position)
			{
			if(!contains(position)) return false;
			if(isForward())
				{
				return	position==getStart() ||
				position==getStart()+1;
	
				}
			else
				{
				return  (position==getEnd()-1) ||
				(position==getEnd()-2);
				}
			}
	
		}


	


	@Override
	public int hashCode() {
	final int prime = 31;
	int result = 1;
	result = prime * result + name.hashCode();
	return result;
	}
	@Override
	public boolean equals(Object obj) {
	if (this == obj)
		return true;
	if (obj == null)
		return false;
	if (getClass() != obj.getClass())
		return false;
	KnownGene other = (KnownGene) obj;
	if (!name.equals(other.name))
		return false;
	return true;
	}

	@Override
	public String toString()
		{
		return getName();
		}

	public int length()
		{
		return getTxEnd()-getTxStart();
		}
	public int getExonCount()
		{
		return this.exonStarts.length;
		}

	public static class AsDom implements DomSerializer<KnownGene>
		{
		private Element create(Document owner,String tag,Object content)
			{
			org.w3c.dom.Element child=owner.createElement(tag);
			child.appendChild(owner.createTextNode(String.valueOf(content)));
			return child;
			}
		@Override
		public Element toDom(KnownGene o, Document owner)
			{
			org.w3c.dom.Element root=owner.createElement("KnownGene");

			
			root.appendChild(create(owner, "name", o.getName()));
			root.appendChild(create(owner, "chrom", o.getChrom()));
		
			root.appendChild(create(owner, "strand", o.getStrand()));
			root.appendChild(create(owner, "txStart", o.getTxStart()));
			root.appendChild(create(owner, "txEnd", o.getTxEnd()));
			
			root.appendChild(create(owner, "cdsStart", o.getCdsStart()));
			root.appendChild(create(owner, "cdsEnd", o.getCdsEnd()));
			
			

			org.w3c.dom.Element exons=owner.createElement("exons");
			root.appendChild(exons);
			exons.setAttribute("count", String.valueOf(o.getExonCount()));
			for(int i=0;i< o.getExonCount();++i)
				{
				org.w3c.dom.Element exon=owner.createElement("Exon");
				exons.appendChild(exon);

				org.w3c.dom.Element p=owner.createElement("start");
				p.appendChild(owner.createTextNode(String.valueOf(o.getExonStart(i))));
				exon.appendChild(p);
				
				p=owner.createElement("end");
				p.appendChild(owner.createTextNode(String.valueOf(o.getExonEnd(i))));
				exon.appendChild(p);
				}


			return root;
			}
		}
	

	public static class StaxSerializer
	implements XmlStreamSerializer<KnownGene>
		{
		@Override
		public void write(KnownGene o,javax.xml.stream.XMLStreamWriter w) throws javax.xml.stream.XMLStreamException
			{
	
			w.writeStartElement("KnownGene");
			w.writeStartElement("name");
			w.writeCharacters(String.valueOf(o.getName()));
			w.writeEndElement();
			w.writeStartElement("chrom");
			w.writeCharacters(String.valueOf(o.getChrom()));
			w.writeEndElement();
			w.writeStartElement("strand");
			w.writeCharacters(String.valueOf(o.getStrand()));
			w.writeEndElement();
			w.writeStartElement("txStart");
			w.writeCharacters(String.valueOf(o.getTxStart()));
			w.writeEndElement();
			w.writeStartElement("txEnd");
			w.writeCharacters(String.valueOf(o.getTxEnd()));
			w.writeEndElement();
			w.writeStartElement("cdsStart");
			w.writeCharacters(String.valueOf(o.getCdsStart()));
			w.writeEndElement();
			w.writeStartElement("cdsEnd");
			w.writeCharacters(String.valueOf(o.getCdsEnd()));
			w.writeEndElement();
	
	
			w.writeStartElement("exons");
			w.writeAttribute("count", String.valueOf(o.getExonCount()));
			for(int i=0;i< o.getExonCount();++i)
				{
				w.writeStartElement("Exon");
				w.writeAttribute("name","Exon "+(o.getStrand()=='+'?String.valueOf(i+1):String.valueOf(o.getExonCount()-i)));
				w.writeStartElement("start");
				w.writeCharacters(String.valueOf(o.getExonStarts()[i]));
				w.writeEndElement();
				w.writeStartElement("end");
				w.writeCharacters(String.valueOf(o.getExonEnds()[i]));
				w.writeEndElement();
				w.writeEndElement();
				}
			w.writeEndElement();
	
			w.writeEndElement();
			}
		}


	public static class Binding
	extends TupleBinding<KnownGene>
		{	
		public Binding()
			{
			}
		@Override
		public KnownGene entryToObject(TupleInput in)
			{
			KnownGene kg=new KnownGene();
			kg.name=in.readString();
			kg.chrom=in.readString();
			kg.strand=in.readChar();
			kg.txStart=in.readInt();
			kg.txEnd=in.readInt();
			kg.cdsStart=in.readInt();
			kg.cdsEnd=in.readInt();
			int nExons=in.readInt();
			kg.exonStarts=new int[nExons];
			kg.exonEnds=new int[nExons];
			for(int i=0;i< nExons;++i)
				{
				kg.exonStarts[i]=in.readInt();
				kg.exonEnds[i]=in.readInt();
				}
			return kg;
			}
		@Override
		public void objectToEntry(KnownGene kg, TupleOutput out)
			{
			out.writeString(kg.name);
			out.writeString(kg.chrom);
			out.writeChar(kg.strand);
			out.writeInt(kg.txStart);
			out.writeInt(kg.txEnd);
			out.writeInt(kg.cdsStart);
			out.writeInt(kg.cdsEnd);
			out.writeInt(kg.getExonCount());
			for(int i=0;i< kg.getExonCount();++i)
				{
				out.writeInt(kg.exonStarts[i]);
				out.writeInt(kg.exonEnds[i]);
				}
			}
		}


	public static KnownGene parse(ResultSet row)
	throws SQLException
		{
		KnownGene g=new KnownGene();
		g.name= row.getString("name");
		g.chrom=row.getString("chrom");
		g.strand=row.getString("strand").charAt(0);
		g.txStart=row.getInt("txStart");
		g.txEnd=row.getInt("txEnd");
		g.cdsStart=row.getInt("cdsStart");
		g.cdsEnd=row.getInt("cdsEnd");
		int exons=row.getInt("exonCount");
		g.exonStarts=new int[exons];
		g.exonEnds=new int[exons];
		String ss[]=row.getString("exonStarts").split(",");
		for(int i=0;i< exons;++i) g.exonStarts[i]=Integer.parseInt(ss[i]);
		ss=row.getString("exonEnds").split(",");
		for(int i=0;i< exons;++i) g.exonEnds[i]=Integer.parseInt(ss[i]);
		return g;
		}

	public static KnownGene parse(String tokens[])
		{
		KnownGene g=new KnownGene();
		g.name=tokens[0];
		g.chrom=tokens[1];
		g.strand=tokens[2].charAt(0);
		g.txStart=Integer.parseInt(tokens[3]);
		g.txEnd=Integer.parseInt(tokens[4]);
		g.cdsStart=Integer.parseInt(tokens[5]);
		g.cdsEnd=Integer.parseInt(tokens[6]);
		int exons=Integer.parseInt(tokens[7]);
		g.exonStarts=new int[exons];
		g.exonEnds=new int[exons];
		String ss[]=tokens[8].split(",");
		for(int i=0;i< exons;++i) g.exonStarts[i]=Integer.parseInt(ss[i]);
		ss=tokens[9].split(",");
		for(int i=0;i< exons;++i) g.exonEnds[i]=Integer.parseInt(ss[i]);
		return g;
		}

	}
